home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
et
/
et3_0-a1.lha
/
et3
/
src
/
Object.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-07-23
|
16KB
|
721 lines
#ifdef __GNUG__
#pragma implementation
#endif
#include "Object.h"
#include "Class.h"
#include "OrdColl.h"
#include "IdDictionary.h"
#include "ObjInt.h"
#include "Error.h"
#include "String.h"
#include "Storage.h"
#include "ProgEnv.h"
#include "System.h"
#include "ObjectTable.h"
#include "AccessMem.h"
#include "MemBuf.h"
#include "CmdNo.h"
//---- observing ---------------------------------------------------------
static IdDictionary *Observer;
// dictionary associating a list of observers with an Object
static IdDictionary *DelayedChanges;
// dictionary to store the delayed Change messages, the key is the object pointer
// (IdDictionary) the value is an OrderedCollection of ChangeMessages (see below).
// The first entry of the OrderedCollection is a reference count of the number of
// DelayChanges requests. FlushChanges will be executed when the count drops to 0.
// Whether change messages are delayed is set with the cObjDelayChanges flag
ONEXIT(Object)
{
if (Observer) {
Observer->FreeValues();
SafeDelete(Observer);
}
if (DelayedChanges) {
DelayedChanges->FreeAllValues();
SafeDelete(DelayedChanges);
}
}
class ChangeMessage: public Object {
public:
void *what;
int part, id;
public:
MetaDef(ChangeMessage);
ChangeMessage(void *w, int p, int i)
{ what= w; part= p; id= i; }
};
NewMetaImpl(ChangeMessage,Object, (TX(what), T(part), T(id)));
extern u_long Storage_lastobj, Storage_lastobjend;
extern bool Storage_proto;
//---- Object ------------------------------------------------------------------
int Object::_an;
AccessMembers *Object::_accessMembers;
//---- automatically added by macro metaImpl -----------------------------------
Class *Object::isa;
static Class ObjectClassMetaImpl0("Object",
sizeof (Object),
new Object((_dummy*)0),
__FILE__,
"./Object.h",
__LINE__,
1,
TRUE);
Object::Object(class _dummy*)
{
Storage_proto= TRUE;
flags= cObjNonDeleted | cObjIsProto;
if ((u_long)this >= Storage_lastobj && (u_long)this < Storage_lastobjend)
SetFlag(cObjIsShared);
isa= &ObjectClassMetaImpl0;
ObjectClassMetaImpl0.SetSuper();
}
Class *Object::IsA()
{
return &ObjectClassMetaImpl0;
}
void Object::Members(AccessMembers *ac)
{
_accessMembers= ac;
ScanMembers(TX(flags), TP(Observer), TP(DelayedChanges), TP(isa));
}
//---- ordinary methods ----------------------------------------------------
Object::Object(int f)
{
flags= (f & cFlagMask) | cObjNonDeleted;
if ((u_long)this >= Storage_lastobj && (u_long)this < Storage_lastobjend) {
SetFlag(cObjIsShared);
if (Storage_proto)
flags|= cObjIsProto;
}
ObjectTable::Add(this);
}
Object::~Object()
{
if (! TestFlag(cObjNonDeleted))
Fatal("~Object", "object deleted twice");
if (IsObserved()) {
Collection *observers= GetObservers(); // GetObservers is statically bound!!!
flags &= ~cObjIsObserved;
if (observers)
CleanupObservers(observers);
}
flags&= ~cObjNonDeleted;
ObjectTable::Remove(this);
}
void Object::FreeAll()
{
}
//----- flag manipulation ------------------------------------------------------
void Object::SetFlag(int f, bool b)
{
if (b)
SetFlag(f);
else
ResetFlag(f);
}
//---- attributes --------------------------------------------------------------
char *Object::ClassName()
{
return IsA()->Name();
}
//---- comparing ---------------------------------------------------------------
u_long Object::Hash ()
{
return (u_long) this;
}
bool Object::IsEqual(Object *anOp)
{
return anOp == this;
}
int Object::Compare(Object*)
{
AbstractMethod ("Compare");
return 0;
}
//---- converting, activation/passivation ------------------------------------
char* Object::AsString()
{
return 0;
}
static bool printOnWhenObserved(Object*, Object* op, Object *from)
{
return op->PrintOnWhenObserving(from);
}
OStream &Object::PrintOn(OStream &os)
{
Collection *selectedDeps= 0;
os.MakeIndex(this);
if (IsObserved()) {
Collection *observers= GetObservers();
if (observers->IsEmpty()) { // impossible ??
flags &= ~cObjIsObserved;
DestroyObserverColl();
} else {
selectedDeps= observers->Select((BoolFun)printOnWhenObserved, this);
if (selectedDeps->IsEmpty())
SafeDelete(selectedDeps);
}
}
os << (flags & cFlagMask) SP << selectedDeps SP;
SafeDelete(selectedDeps);
return os;
}
OStream &operator<< (OStream &os, Object *op)
{
int ix;
if ((op == 0) || (op == op->IsA())) { // a null pointer
os.put('0'); // "0" seems to be portable!
} else if ((ix= os.IndexOfPtr(op)) >= 0 ) { // already in table ?
os.put('@');
os << ix; // write index
} else {
Object *op2;
// special case: we are copying inside a single address space
// try if class has overwritten deepclone
if (os.IsForDeepClone() && (op2= op->deepclone())) {
os.put('#');
os << os.MakeIndex(op) SP << (u_long) op2;
} else {
if (! op->IsKindOf(Class))
os.put('\n'); // pretty printing
os.put('{');
os << os.MakeIndex(op) SP << op->IsA();
op->PrintOn(os);
os.put('}');
}
}
os.put(' ');
os.flush();
return os;
}
IStream &Object::ReadFrom(IStream &is)
{
int f;
Collection *observers;
is.MakeIndex(this);
is >> f >> observers;
SetFlag(f);
if (observers) {
flags |= cObjIsObserved;
SetObserverColl(observers);
}
return is;
}
Object *Object::ReadAndMap(IStream &is)
{
Object *op= New();
(*op).ReadFrom(is);
return op;
}
/* Read in new object, put a pointer to it in *op.
* Make sure object read in is cl or a descendant of cl.
*/
IStream& LoadPtr(IStream &is, Object *&op, Class *expected)
{
static char *cLoadError= "Object::LoadPtr";
Class *clp= 0;
char c= 0;
int ix= 0;
u_long ptr= 0;
op= 0;
is >> c;
switch (c) {
case '0': // a nil pointer
return is;
case '#': // a real address from DeepCopy
is >> ix >> ptr;
if (is.IsForDeepClone()) {
op= (Object*) ptr;
is.AtPut(ix, op);
} else
Error(cLoadError, "shouldn't happen: got address %d", ptr);
break;
case '@': // table index of existing object
is >> ix;
op= is.At(ix);
if (op == (Object*) 0x01) {
Error(cLoadError, "object is 1", ix);
op= 0;
} else if (op == 0)
Error(cLoadError, "can't find element %d in instancetable", ix);
break;
case '{': // new object
is >> ix >> clp;
if (clp == 0) // assume class "Class"
clp= Meta(Class);
op= clp->Proto()->ReadAndMap(is);
is.AtPut(ix, op);
int level= 1;
bool first= TRUE;
while (is.get(c)) {
if (c == '{') {
level++;
continue;
}
if (c == '}') {
level--;
if (level <= 0)
break;
continue;
}
if (!Isspace(c)) {
if (first) {
Error(cLoadError, "expected \'}\' at end of <%s>; got: (%02x)",
clp->Name(), c);
first= FALSE;
}
}
};
break;
default:
Error(cLoadError, "unexpected character 0x%02x", c & 0xff);
break;
}
if (op == 0) {
if (expected) {
Error(cLoadError, "can't read object; creating dummy instance of %s",
expected->Name());
op= expected->Proto()->New();
}
} else if (expected) {
Class *clp= op->IsA();
if (clp && !clp->isKindOf(expected))
Error(cLoadError, "trying to read a %s into a %s", clp->Name(),
expected->Name());
}
return is;
}
//---- cloning -----------------------------------------------------------------
Object *Object::New()
{
Object *op= IsA()->Proto()->Object::Clone();
op->InitNew();
return op;
}
void Object::InitNew()
{
}
Object *Object::Clone()
{
Object *op= (Object*) Storage::ObjectAlloc(IsA()->Size());
// give the new object the correct vtable in a portable? way
memcpy(op, this, IsA()->Size());
op->flags |= cObjNonDeleted | cObjIsShared;
op->flags &= ~cObjIsProto;
ObjectTable::Add(op);
return op;
}
Object *Object::deepclone()
{
return 0;
}
Object *Object::DeepClone()
{
Object* op;
if (op= deepclone())
return op;
MemBuf mb;
OStream to(&mb);
IStream from(&mb);
to.PrepareForDeepClone();
to << this;
to.flush();
mb.SwitchToRead();
from.PrepareForDeepClone();
from >> op;
return op;
}
//---- change propagation, object observing ------------------------------------
Collection *Object::MakeObserverColl()
{
Collection *cp;
if (Observer == 0)
Observer= new IdDictionary(70);
if (cp= (Collection*) Observer->AtKey(this))
Warning("MakeObserverColl", "there are already Observers for %s", ClassName());
else {
cp= new OrdCollection;
Observer->PutAtKeyIfAbsent(cp, this);
}
return cp;
}
Collection *Object::GetObservers()
{
if (Observer == 0) {
Warning("GetObservers", "without SetObserver");
return 0;
}
Collection *cp= (Collection*) Observer->AtKey(this);
if (cp == 0)
Warning("GetObservers", "observers == 0 in GetObservers for %s", ClassName());
return cp;
}
void Object::DestroyObserverColl()
{
#ifdef OLD
if (Observer) {
Assoc *ap= (Assoc*) Observer->RemoveKey(this);
if (ap) {
Object *observers= ap->Value();
if (observers) {
SafeDelete(observers);
} else
Warning ("DestroyObserverColl", "0 in DestroyObserverColl() for %s",
ClassName());
SafeDelete(ap);
}
} else
Warning("DestroyObserverColl", "Observer == 0");
#endif
if (Observer) {
Object *value;
if (Observer->RemoveKey(this, value)) {
// could also check for IsObserved
if (value) {
SafeDelete(value); // value is a container
} else
Warning("DestroyObserverColl", "0 in DestroyObserverColl() for %s",
ClassName());
}
} else
Warning("DestroyObserverColl", "Observer == 0");
}
void Object::SetObserverColl(Collection *cp)
{
DestroyObserverColl();
if (Observer == 0)
Observer= new IdDictionary(70);
Observer->PutAtKeyIfAbsent(cp, this);
}
void Object::CleanupObservers(Collection *observers)
{
observers->ForEach(Object,DoObserve)(cIdNone, cPartSenderDied, 0, this);
DestroyObserverColl();
}
void Object::AddObserver(Object* op)
{
Collection *observer;
if (op) {
if (!IsObserved() || ((observer= GetObservers()) == 0)) {
flags |= cObjIsObserved;
observer= MakeObserverColl();
} else {
if (observer->FindPtr(op)) {
Warning("AddObserver", "duplicate in observers");
return;
}
}
observer->Add(op);
}
}
Object* Object::RemoveObserver(Object* op)
{
if (this && IsObserved()) {
Collection *observers= GetObservers();
if (observers) {
if (op)
op= observers->RemovePtr(op);
if (observers->IsEmpty()) {
flags &= ~cObjIsObserved;
DestroyObserverColl();
}
return op;
}
}
return 0;
}
void Object::Send(int id, int part, void *vp)
{
if (!IsObserved())
return;
if (TestFlag(cObjDelayChanges)) {
if (DelayedChanges) {
Collection *col= (Collection*) DelayedChanges->AtKey(this);
if (col == 0) {
Warning("Send", "No collection for change messages, %s", ClassName());
col= new OrdCollection;
DelayedChanges->PutAtKeyIfAbsent(col, this);
}
col->Add(new ChangeMessage(vp, part, id));
} else
Error("Send", "DelayedChanges still 0");
} else {
Collection *observers= GetObservers();
if (observers)
observers->ForEach(Object,DoObserve)(id, part, vp, this);
}
}
void Object::DoObserve(int, int, void*, Object*)
{
}
class Iterator *Object::GetObserverIter()
{
if (!IsObserved())
return 0;
Collection *observers= GetObservers();
if (observers)
return observers->MakeIterator();
return 0;
}
void Object::DelayChanges()
{
SeqCollection *col= 0;
flags |= cObjDelayChanges;
if (DelayedChanges == 0)
DelayedChanges= new IdDictionary;
else
col= (SeqCollection*) DelayedChanges->AtKey(this);
if (col) {
ObjInt *ref= (ObjInt*) col->At(0);
++(*ref);
} else {
col= new OrdCollection;
col->Add(new ObjInt(0));
DelayedChanges->PutAtKeyIfAbsent(col, this);
}
}
void Object::FlushChanges()
{
if (DelayedChanges) {
SeqCollection *col= (SeqCollection*) DelayedChanges->AtKey(this);
if (col) {
ObjInt *ref= (ObjInt*)col->At(0);
if (ref->GetValue() <= 0) {
flags &= ~cObjDelayChanges;
ChangeMessage *cm;
Iter next(col);
next(); // overread reference count
while (cm= (ChangeMessage*) next())
Send(cm->id, cm->part, cm->what);
DelayedChanges->RemoveKey(this);
col->FreeAll();
SafeDelete(col);
} else
(*ref)--;
}
}
}
bool Object::PrintOnWhenObserving(Object*)
{
return TRUE;
}
//---- error handling ----------------------------------------------------------
void Object::DoError(int level, char *location, char *fmt, va_list va)
{
::ErrorHandler(level, form("%s::%s", ClassName(), location), fmt, va);
}
void Object::Error(char *location, char *va_(fmt), ...)
{
va_list ap;
va_start(ap, va_(fmt));
DoError(cError, location, va_(fmt), ap);
va_end(ap);
}
void Object::SysError(char *location, char *va_(fmt), ...)
{
va_list ap;
va_start(ap, va_(fmt));
DoError(cSysError, location, va_(fmt), ap);
va_end(ap);
}
void Object::Warning(char *location, char *va_(fmt), ...)
{
va_list ap;
va_start(ap, va_(fmt));
DoError(cWarning, location, va_(fmt), ap);
va_end(ap);
}
void Object::Fatal(char *location, char *va_(fmt), ...)
{
va_list ap;
va_start(ap, va_(fmt));
DoError(cFatal, location, va_(fmt), ap);
va_end(ap);
}
//---- fire walls --------------------------------------------------------------
void Object::AbstractMethod(char *method)
{
Warning(method, "abstract method called");
}
void Object::MayNotUse(char *method)
{
Warning(method, "not allowed to use this method");
}
Object *Object::guard(Class *should)
{
if (this && IsA()->isKindOf(should))
return this;
if (this)
Error("Guard", "object is not a %s but a %s", should->Name(), ClassName());
return 0;
}
//---- inspecting, debugging ---------------------------------------------------
void Object::Inspect(bool block)
{
gProgEnv->InspectObject(this, block);
}
void Object::EditSource(bool definition)
{
gProgEnv->EditSourceOf(IsA(), definition);
}
void Object::InspectorId(char *buf, int bufSize)
{
char *name= AsString();
if (name)
strn0cpy(buf, name, bufSize);
}
void Object::CollectParts(class Collection *)
{
}
void Object::ScanMembers(u_long va_(firstTag), ...)
{
int n= _an;
_an= 0;
va_list ap;
va_start(ap, va_(firstTag));
if (_accessMembers)
_accessMembers->ScanMembers(this, (int)va_(firstTag), ap, n);
va_end(ap);
}
//---- interface for accessing instance variables by name will be improved
// in a future release
class FindMember: public AccessMembers {
public:
const char *lookfor;
int offset, len, type;
Object *op;
Class *classp;
FindMember(Object *in, const char *name)
{ offset= -1; op= in; lookfor= name; type= 0; len= -1;}
void Member(char *name, int offset, int lenOrOffset, int type,
Class*, bool g, bool lg);
};
void FindMember::Member(char *name, int o, int, int t, Class *c, bool ,
bool)
{
if (strcmp(name, lookfor) == 0) {
offset= o;
type= t;
classp= c;
// if (l == -1)
// len= GetLength(op, o, l, t, c, g, lg);
}
}
void *Object::GetAddrOf(const char *name, Class *&cl)
{
FindMember m(this, name);
IsA()->EnumerateMembers(&m);
if (m.offset != -1) {
cl= m.classp;
return (void*) ((u_long) this + (u_long) m.offset);
}
return 0;
}